﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Netfraction.Common;

namespace Netfraction.LocalMachine
{
    public class ClientIdEventArgs : EventArgs
    {
        public string PrivateId { get; set; }
        public string ClientId { get; set; }
        
        public ClientIdEventArgs(string privateId, string clientId)
        {
            this.PrivateId = privateId;
            this.ClientId = clientId;
        }
    }

    public delegate void ClientIdEventHandler(ClientIdEventArgs e);

    public class Client
    {
        // Use singleton pattern.
        private static Client _instance;

        public static event ClientIdEventHandler OnClientIdSet;

        public static void ClientIdSet(string privateId, string clientId)
        {
            if (OnClientIdSet != null)
                OnClientIdSet(new ClientIdEventArgs(privateId, clientId));
        }

        private static Managers.PeerManager _peerManager;

        private string _clientId;
        private string _privateID;

        protected Client()
        {
            this.Features = new Dictionary<string, Netfraction.Network.Protocol.ProtocolFeature>();
            _peerManager = Managers.PeerManager.GetInstance();
            _clientId = string.Empty;

            InitialiseFeatures();
        }

        private void InitialisePrivateId()
        {
            byte[] seed = new byte[24];
            Random rand = new Random();
            rand.NextBytes(seed);

            _privateID = Base32.Encode(seed).Substring(0, 39);
        }

        public void InitializeClientId(System.Security.Cryptography.HashAlgorithm hashAlgorithm)
        {
            // I might be talking about the Private ID in these 3 lines... yea pretty sure I am. generate CID off PID w/ session hash (coz the SH can change). read PID from xml settings
            // should only ever be generated once, unless asked to regenerate, because the CID is used to register users on the hub.
            // what happens with banned users though, how are they banned if CID can be regen'd?
            // load from xml settings anyway, with the rest of the client.Identity settings

            if (string.IsNullOrEmpty(_clientId))
            {
                hashAlgorithm.Initialize();
                byte[] hash = hashAlgorithm.ComputeHash(Base32.Decode(_privateID));
                _clientId = Base32.Encode(hash).Substring(0, 39);

                ClientIdSet(_privateID, _clientId);
            }
        }

        public void SetIdentity(Identity clientIdentity)
        {
            RemoteMachine.Peer peer = new RemoteMachine.Peer();
            peer.IsLocalClient = true;
            peer.Identity = clientIdentity;
            _peerManager.Add(peer);
        }

        private void InitialiseFeatures()
        {
            lock (this.Features)
            {
                this.Features.Add(Network.Protocol.ProtocolFeature.GetFeatureName(typeof(Network.Protocol.BASE.ProtocolFeatureBASE)), new Network.Protocol.BASE.ProtocolFeatureBASE());
                this.Features.Add(Network.Protocol.ProtocolFeature.GetFeatureName(typeof(Network.Protocol.TIGR.ProtocolFeatureTIGR)), new Network.Protocol.TIGR.ProtocolFeatureTIGR());
                this.Features.Add(Network.Protocol.ProtocolFeature.GetFeatureName(typeof(Network.Protocol.BZIP.ProtocolFeatureBZIP)), new Network.Protocol.BZIP.ProtocolFeatureBZIP());
                this.Features.Add(Network.Protocol.ProtocolFeature.GetFeatureName(typeof(Network.Protocol.UCMD.ProtocolFeatureUCMD)), new Network.Protocol.UCMD.ProtocolFeatureUCMD());
                this.Features.Add(Network.Protocol.ProtocolFeature.GetFeatureName(typeof(Network.Protocol.ADCS.ProtocolFeatureADCS)), new Network.Protocol.ADCS.ProtocolFeatureADCS());
            }
        }

        public Managers.PeerManager Peers
        {
            get { return _peerManager; }
            set { _peerManager = value; }
        }

        public Identity Identity
        {
            get
            {
                if (_peerManager.Count == 0)
                    return null;

                return _peerManager[_clientId].Identity;
            }
            set
            {
                if (_peerManager.Count > 0)
                    _peerManager[_clientId].Identity = value;
            }
        }

        public Dictionary<string, Network.Protocol.ProtocolFeature> Features { get; set; }

        /// <summary>
        /// Obtain singleton instance of Client
        /// </summary>
        /// <returns></returns>
        public static Client GetInstance()
        {
            return GetInstance(string.Empty);
        }

        /// <summary>
        /// Obtain singleton instance of Client
        /// </summary>
        /// <returns></returns>
        public static Client GetInstance(string privateId)
        {
            // Check instance
            if (Client._instance == null)
            {
                // Initialize instance
                Client._instance = new Client();

                if (privateId.Length == 39) // check Base32 encoded
                    _instance._privateID = privateId;
                else
                    _instance.InitialisePrivateId();
            }

            // Return instance
            return Client._instance;
        }
    }
}
